from machine import UART, Pin
from utils import device, format, platform
from lib.config.index import sensor_config, wifi_config
from lib.net.wlan import wlan_base
from lib.clock import ticker_clock
from lib.log.index import  exception_logger


class WLAN(wlan_base.WLAN):
    def __init__(self):
        super().__init__()
        self.__rx = None
        self.__tx = None
        self.__uart = None
        self.__established = False
        self.init_uart()

    def init_uart(self):
        at_config = sensor_config.get_sensor("esp8266")
        self.__rx = at_config["rx"]
        self.__tx = at_config["tx"]

        if self.__uart:
            print("AT UART has been inited")
            return

        # rp2350必须是0,RP2040可以为1
        if platform.is_raspberry_pico():
            self.__uart = UART(1, baudrate=115200, tx=Pin(self.__tx), rx=Pin(self.__rx))

        if platform.is_raspberry_pico2():
            self.__uart = UART(0, baudrate=115200, tx=Pin(self.__tx), rx=Pin(self.__rx))

    def write_uart(self, buf):
        if not self.__uart:
            if wifi_config.is_debug():
                print("wlan at: ", "uart not inited")
            return
        if wifi_config.is_debug():
            print("wlan at write: ", buf)
        self.__uart.write(buf)

    def wait_uart_data(self, wait, format_type = "raw"):
        timer = ticker_clock.TickerClock(wait)
        buf = bytearray()
        while True:
            if self.__uart.any():
                data = self.__uart.read()
                buf = buf + data
            if timer.check():
                break
        if  wifi_config.is_debug():
            print("wlan at read: ", buf)

        try:
            buf_str = buf.decode('ascii')
            if "ERROR" in buf_str:
                self.reconnect()
            if "busy" in buf_str:
                self.reconnect()
            if "SEND FAIL" in buf_str:
                self.reconnect()
        except Exception as e:
            exception_logger.log(e)
            exception_logger.log(buf)
            self.reconnect()
            return ""

        if format_type == "raw":
            return buf
        return format.transform_response(buf)

    def connect_server(self, domain, port):
        if not self.__established:
            cmd = 'AT+CIPSTART="TCP","' + domain + f'",{port}\r\n'
            self.write_uart(cmd)
            self.wait_uart_data(3000)
            self.__established =True
        else:
            if wifi_config.is_debug():
                print("wlan at: connection has been established, manual disconnect needed")

    def send_request(self, payload, wait = 2000, format_type = "json"):
        length = len(payload)
        if wifi_config.is_debug():
            print(f"req:({length})",payload)
        self.write_uart(f"AT+CIPSEND={length}\r\n")
        self.wait_uart_data(100)
        self.write_uart(payload)
        return self.wait_uart_data(wait, format_type)

    def reconnect(self):
        if wifi_config.is_debug():
            print("wlan at: reconnect")
        self.__status = WLAN.WLAN_STATUS["disconnected"]
        self.connect()

    def connect(self):
        super().connect()
        if self.is_connected() or self.is_connecting():
            return
        
        cmds = [
            ("AT+RESTORE\r\n", 2000),
            #("AT+RST\r\n", 600),
            ("AT+CWMODE=1\r\n", 100),
            ("AT+CIPDINFO=0\r\n", 100),
            (f'AT+CWJAP="{self.__ssid}","{self.__password}"\r\n', 8000),
            (f"AT+CIPSTA?\r\n", 500),
        ]

        for cmd in cmds:
            self.write_uart(cmd[0])
            self.wait_uart_data(cmd[1])

        self.__status = WLAN.WLAN_STATUS["connected"]
        device.update_net_info()

    def disconnect(self):
        super().disconnect()
        cmds = [
            ("AT+CIPCLOSE\r\n", 100)
        ]
        for cmd in cmds:
            self.write_uart(cmd[0])
            self.wait_uart_data(cmd[1])
        self.__status = WLAN.WLAN_STATUS["disconnected"]
        self.__established = False

    def is_connected(self):
        return self.__status == WLAN.WLAN_STATUS["connected"]

    def is_connecting(self):
        return self.__status == WLAN.WLAN_STATUS["pending"]

    def scan(self):
        super().scan()
        self.write_uart("AT+CWLAPOPT=1,31\r\n")
        self.wait_uart_data(200)
        self.write_uart("AT+CWLAP\r\n")
        scan_res = self.wait_uart_data(3000)
        return scan_res
